﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;
using Utilities;
using Services;
using System.Threading;

namespace ChainAlg
{
    public class BodyData
    {
        public Vector3 pos;
        public float range;
        public int index;
        public bool reachead;
        public List<BodyData> childs = new List<BodyData>();
    }
    static class Alg
    {
        
        public static int MAX_ITERATION = 100000;
        public static void computeList(List<BodyData> list, int width, int height, int count, int junk_elements, int addictionalStartPoints, int paths, Action<bool> end)
        {
            //List<BodyData> lastBodies = new List<BodyData>();
            Random rnd = new Random();
            var delta_range = 90 / count;
            //float range = 110;
            list.Clear();
            list.Add(new BodyData()
            {
                range = 100,
                pos = new Vector3(rnd.Next(width), 0, rnd.Next(height)),
                index = 0,
                reachead=false
            });
            //Thread.Sleep(1000);
            count--;
            //range -= delta_range;

            for(int i=0;i<paths;i++)
                AddPathFromStartPoint(list, count / paths, delta_range, rnd, width, height, end, list[0]);
            //AddPathFromStartPoint(list, count / 2, delta_range, rnd, width, height, end, list[0]);
            var startPoint = list[0];
            for (int c = 0; c < addictionalStartPoints; c++)
            {
                var newPos = getVect(startPoint.pos, startPoint.range, startPoint.range / 2, rnd, width, height);
                var newObj = new BodyData()
                {
                    range = Vector3.Distance(startPoint.pos, newPos) * 11 / 10,
                    pos = newPos,
                    index = count + c
                };
                startPoint.childs.Add(newObj);
                list.Add(newObj);
            }

            for (int c = 0; c < junk_elements; c++)
            {
                BodyData old = list[rnd.Next(1000) % list.Count];
                if (old.index >= count+addictionalStartPoints)
                {
                    c--;
                    continue;
                }
                Vector3 newPos = getVect(old.pos, old.range, old.range / 3, rnd, width, height);
                float range = Vector3.Distance(old.pos, newPos) / 2;
                for (int i = 0; i < list.Count; i++)
                {
                    var dist = Vector3.Distance(newPos, list[i].pos);
                    if (dist < range)
                        range = dist / 2;
                }
                var newObj=new BodyData()
                {
                    pos = newPos,
                    index = count + addictionalStartPoints + c,
                    range = range,
                    reachead = false
                };
                list.Add(newObj);
                old.childs.Add(newObj);
            }

            end(true);
        
        }

        public static void AddPathFromStartPoint(List<BodyData> list, int count, float delta_range, Random rnd, int width, int height, Action<bool> end, BodyData startPoint)
        {
            for (int c = 0; c < count; c++)
            {
                BodyData old;
                
                if (c != 0)
                    old = list[list.Count - 1];
                else
                    old = startPoint;
                //else
                //{
                //    old = lastBodies[0];
                //    lastBodies.RemoveAt(0);
                //}
                bool ok = true;
                int times = 0;
                Vector3 pos;
                do
                {
                    pos = getVect(old.pos, old.range*0.9f, old.range - delta_range, rnd, width, height);
                    times++;

                    //ok = pos.X>=0 && pos.X<width && pos.Z>=0 && pos.Z<height;
                    //if (!ok)
                    //    break;
                    ok = true;
                    for (int i = 0; i < list.Count - 1 && ok; i++)
                    {
                        //if (list[i].index == old.index)
                        //    continue;
                        var dist = Vector3.Distance(list[i].pos, pos);
                        if (dist < list[i].range*0.9f)
                        {
                            ok = false;
                        }
                    }
                    if (times > MAX_ITERATION)
                        end(false);
                }
                while (!ok);
                var newBody = new BodyData()
                {
                    pos = pos,
                    range = old.range - delta_range,
                    index = c,
                    reachead = false
                };
                list.Add(newBody);
                old.childs.Add(newBody);
            }
        }

        public static Vector3 getVect(Vector3 center, float max_d, float min_d, Random rnd, int width, int height)
        {
            //rnd = new Random();
            var rot = MathHelper.ToRadians(rnd.Next(360));
            var offset = rnd.NextFloat(max_d, min_d);
            var pos = center + Vector3.UnitX * offset * (float)Math.Cos(rot) + Vector3.UnitZ * offset * (float)Math.Sin(rot);
            if (!(pos.X >= 0 && pos.X < width && pos.Z >= 0 && pos.Z < height))
                return getVect(center, max_d, min_d, rnd, width, height);
            return pos;
        }
    }

    static class RandomExtensions
    {
        public static float NextFloat(this Random rnd, float min, float max)
        {
            int x = rnd.Next(100);
            return min + (max - min) * x / 100;
        }
    }
}
